<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        //任务队列分为 macrotasks 和 microtasks，而Promise中的then方法的函数会被推入 microtasks 队列，
        //而setTimeout的任务会被推入 macrotasks 队列。在每一次事件循环中，macrotask 只会提取一个执行，
        //而 microtask 会一直提取，直到 microtasks 队列清空。

        /**
         * nextTick 可以让我们在下次 DOM 更新循环结束之后执行延迟回调，用于获得更新后的 DOM。
           在 Vue 2.4 之前都是使用的 microtasks，但是 microtasks 的优先级过高，在某些情况下可能会
           出现比事件冒泡更快的情况，但如果都使用 macrotasks 又可能会出现渲染的性能问题。
           所以在新版本中，会默认使用 microtasks，但在特殊情况下会使用 macrotasks，比如 v-on。
         * 
         */


        //对于实现 macrotasks ，会先判断是否能使用 setImmediate ，不能的话降级为 MessageChannel ，
        //以上都不行的话就使用 setTimeout
        if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
            macroTimerFunc = () => {
                setImmediate(flushCallbacks)
            }
        } else if (
            typeof MessageChannel !== 'undefined' &&
            (isNative(MessageChannel) ||
                // PhantomJS
                MessageChannel.toString() === '[object MessageChannelConstructor]')
        ) {
            const channel = new MessageChannel()
            const port = channel.port2
            channel.port1.onmessage = flushCallbacks
            macroTimerFunc = () => {
                port.postMessage(1)
            }
        } else {
            /* istanbul ignore next */
            macroTimerFunc = () => {
                setTimeout(flushCallbacks, 0)
            }
        }

        //nextTick 同时也支持 Promise 的使用，会判断是否实现了 Promise
        export function nextTick(cb?: Function, ctx?: Object) {
            let _resolve
            // 将回调函数整合进一个数组中
            callbacks.push(() => {
                if (cb) {
                    try {
                        cb.call(ctx)
                    } catch (e) {
                        handleError(e, ctx, 'nextTick')
                    }
                } else if (_resolve) {
                    _resolve(ctx)
                }
            })
            if (!pending) {
                pending = true
                if (useMacroTask) {
                    macroTimerFunc()
                } else {
                    microTimerFunc()
                }
            }
            // 判断是否可以使用 Promise 
            // 可以的话给 _resolve 赋值
            // 这样回调函数就能以 promise 的方式调用
            if (!cb && typeof Promise !== 'undefined') {
                return new Promise(resolve => {
                    _resolve = resolve
                })
            }
        }



        // console.log('script start');
        // setTimeout(function () {
        //     console.log('setTimeout');
        // }, 0);

        // Promise.resolve().then(function () {
        //     console.log('promise');
        // }).then(function () {
        //     console.log('promise2');
        // });

        // console.log('script end');
        //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓打印顺序
        // script start
        // script end
        // promise
        // promise2
        // setTimeout
    </script>
</body>

</html>